﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krilib.Data;


namespace Krile.Cores
{
    public class KeyAssign
    {
        public KeyAssign()
        {
            assignDatas = new Dictionary<Keys, Krile.Cores.KeyAssignElems.KeyAssignData>();
            candidates = new List<Krile.Cores.KeyAssignElems.KeyAssignCallbackCandidate>();
        }

        /// <summary>
        /// Set main form's key event for ready to key assign system
        /// </summary>
        /// <param name="MainForm"></param>
        public void SetKeyEvent(Forms.MainForm.Main MainForm)
        {
            MainForm.KeyPreview = true;
            MainForm.PreviewKeyDown += new PreviewKeyDownEventHandler(MainForm_PreviewKeyDown);
            MainForm.KeyDown += new KeyEventHandler(MainForm_KeyDown);
        }

        //mainform event handler
        void MainForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                case Keys.Enter:
                case Keys.Escape:
                case Keys.ShiftKey:
                    e.IsInputKey = true;
                    break;
            }
        }
        void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (Krile.Imported.Snippets.WinForms.GetDeepestActiveControl(Core.FormManager.MainForm).GetType() == typeof(TextBox)) return;

            var lookup = LookupAssignData(e.KeyData);
            if (lookup != null)
            {
                if (Core.FormManager.MainForm.SelectedXStatus == null)
                    lookup.InvokeAll(null, e.KeyData);
                else
                    lookup.InvokeAll(Core.FormManager.MainForm.SelectedXStatus.BaseStatus, e.KeyData);
            }
        }

        /// <summary>
        /// Key assign dict
        /// </summary>
        Dictionary<Keys, KeyAssignElems.KeyAssignData> assignDatas;

        /// <summary>
        /// Defreeze assign datas
        /// </summary>
        internal void DefreezeAssigns()
        {
            foreach (var adata in assignDatas.Values)
            {
                adata.DefreezeAll();
            }
        }

        /// <summary>
        /// Key assign candidate elements
        /// </summary>
        List<KeyAssignElems.KeyAssignCallbackCandidate> candidates;

        /// <summary>
        /// Get candidate array
        /// </summary>
        public KeyAssignElems.KeyAssignCallbackCandidate[] GetCandidatesArray()
        {
            return candidates.ToArray();
        }

        /// <summary>
        /// Get assign data array(for external memento pattern)
        /// </summary>
        internal KeyAssignElems.KeyAssignData[] AssignDataArray
        {
            get
            {
                List<KeyAssignElems.KeyAssignData> rets = new List<Krile.Cores.KeyAssignElems.KeyAssignData>();
                foreach (var val in assignDatas.Values)
                {
                    rets.Add(val);
                }
                return rets.ToArray();
            }

            set
            {
                if (value == null) return;
                assignDatas.Clear();
                foreach (var val in value)
                {
                    assignDatas.Add(val.LinkedKeys, val);
                }
            }
        }

        /// <summary>
        /// Lookup key assign data from key
        /// </summary>
        /// <param name="key">key</param>
        /// <returns>assign data or null</returns>
        internal KeyAssignElems.KeyAssignData LookupAssignData(Keys key)
        {
            if (assignDatas.ContainsKey(key))
                return assignDatas[key];
            else
                return null;
        }

        /// <summary>
        /// Lookup callback method
        /// </summary>
        /// <param name="clsname">class name</param>
        /// <param name="method">method</param>
        /// <returns>method or null</returns>
        public Cores.KeyAssignElems.KeyAssignCallbackCandidate LookupCallback(string clsname, string method)
        {
            foreach (var candidate in candidates)
            {
                if (((candidate.CallbackMethod.Target == null && clsname == null) ||
                    candidate.CallbackMethod.Target.GetType().FullName == clsname) &&
                    candidate.CallbackMethod.Method.Name == method)
                {
                    return candidate;
                }
            }
            return null;
        }

        /// <summary>
        /// Get description of method
        /// </summary>
        /// <param name="callbackMethod">target method</param>
        /// <returns>description string or null</returns>
        public string GetDescription(Common.KeyAssignCallback callbackMethod)
        {
            foreach (var candidate in candidates)
            {
                if (candidate.CallbackMethod == callbackMethod)
                {
                    return candidate.Description;
                }
            }
            return null;
        }

        /// <summary>
        /// Add key assign candidate method
        /// </summary>
        /// <param name="addMethod">adding method</param>
        /// <param name="description">method's description</param>
        public void AddAssignCandidateMethod(Common.KeyAssignCallback addMethod, string description)
        {
            candidates.Add(new KeyAssignElems.KeyAssignCallbackCandidate(addMethod, description));
        }
    }
}
